module anem_txt_mod

  use anem_mod
  use datetime
  use string
  use container
  use flogger
  use regex
  use params_mod
  use data_translators_utils_mod
  use cli_mod

  implicit none

  private

  public anem_txt_read

contains

  subroutine anem_txt_read(file_path, towers, records)

    character(*), intent(in) :: file_path
    type(hash_table_type), intent(inout), target :: towers
    type(linked_list_type), intent(inout), target :: records

    type(anem_tower_type), pointer :: tower
    type(anem_record_type), pointer :: record
    type(hash_table_type) local_records
    type(hash_table_iterator_type) record_iterator
    type(datetime_type) time

    character(30) tower_name, time_str
    integer fu, istat, i
    real lon, lat, alt
    real, allocatable :: w_hgt(:)
    real hgt, wd, ws, ws_min, ws_max, ws_std, ta, p, rh

    local_records = hash_table(chunk_size=1000000, max_load_factor=0.9)

    call log_notice('Reading ' // trim(file_path) // ' ...')
    fu = unique_file_number()
    open(fu, file=file_path, status='old')
    read(fu, *) tower_name, lon, lat, alt
    do while (.true.)
      read(fu, *, iostat=istat) tower_name, hgt, time_str, wd, ws, ws_min, ws_max, ws_std, ta, p, rh
      if (istat < 0) exit
      ! Create tower object if needed.
      if (towers%hashed(tower_name)) then
        select type (value => towers%value(tower_name))
        type is (anem_tower_type)
          tower => value
        end select
      else
        allocate(tower)
        call tower%init(tower_name, lon, lat, alt)
        tower%seq_id = towers%size
        call towers%insert(tower_name, tower)
      end if
      ! Create record object if needed.
      if (wd /= 99999.0 .and. ws /= 99999.0) then
        call time%init(time_str(1:19), '%Y-%m-%dT%H:%M:%S')
        record => get_record(local_records, time, tower)
        record%h = hgt
        record%wd_avg = wd
        record%ws_avg = ws
        record%ws_min = ws_min
        record%ws_max = ws_max
        record%ws_std = ws_std
        record%ua_avg = wind_u_component(ws, wd)
        record%va_avg = wind_v_component(ws, wd)
      end if
    end do
    close(fu)

    record_iterator = hash_table_iterator(local_records)
    do while (.not. record_iterator%ended())
      select type (record => record_iterator%value)
      type is (anem_record_type)
        if (cli_start_time%year == 0 .or. (cli_start_time <= record%time .and. record%time <= cli_end_time)) then
          call records%insert(record)
        end if
      end select
      call record_iterator%next()
    end do

    call log_notice('Tower size is ' // trim(to_str(towers%size)) // ', record size is ' // trim(to_str(records%size)) // '.')

  end subroutine anem_txt_read

  function get_record(records, time, tower) result(res)

    type(hash_table_type), intent(inout) :: records
    type(datetime_type), intent(in) :: time
    type(anem_tower_type), intent(in), target :: tower
    type(anem_record_type), pointer :: res

    if (.not. records%hashed(time%isoformat())) then
      allocate(res)
      res%tower => tower
      res%time = time
      call records%insert(time%isoformat(), res)
    end if
    select type (value => records%value(time%isoformat()))
    type is (anem_record_type)
      res => value
    class default
      call log_error('Internal error!', __FILE__, __LINE__)
    end select

  end function get_record

end module anem_txt_mod
